package com.wwj.server.game;

import com.google.gson.Gson;
import com.wwj.core.wawa.entity.PlayRecord;
import com.wwj.core.wawa.entity.Player;
import com.wwj.core.wawa.entity.WaWaMachine;
import com.wwj.core.wawa.service.IPlayRecordService;
import com.wwj.core.wawa.service.IPlayerService;
import com.wwj.core.wawa.service.IWaWaMachineService;
import com.wwj.server.game.common.CmdConstants;
import com.wwj.server.game.common.MsgConstants;
import com.wwj.server.game.msg.*;
import com.wwj.server.game.session.SessionImpl;
import com.wwj.server.game.session.SessionManager;
import com.wwj.server.game.utils.SpringContextUtils;
import com.wwj.server.model.Message;
import com.wwj.server.session.Session;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.net.InetSocketAddress;

/**
 * 消息接受处理类
 *
 * @author tysci
 */
public class ServerHandler extends SimpleChannelInboundHandler<Message> {

    private Logger logger = LoggerFactory.getLogger(getClass());

    private IPlayerService playerService = (IPlayerService) SpringContextUtils.getContext().getBean("playerServiceImpl");

    private IPlayRecordService playRecordService = (IPlayRecordService) SpringContextUtils.getContext().getBean("playRecordServiceImpl");

    private IWaWaMachineService waWaMachineService = (IWaWaMachineService) SpringContextUtils.getContext().getBean("waWaMachineServiceImpl");

    private JedisPool jedisPool = (JedisPool) SpringContextUtils.getContext().getBean("jedisPool");

    //1.连接注册
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
        //logger.debug("host:{} 开始连接本机...", ((InetSocketAddress) ctx.channel().remoteAddress()).getHostName());
    }

    //2.连接激活
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        //logger.debug("host:{} 连接通道已激活...", ((InetSocketAddress) ctx.channel().remoteAddress()).getHostName());
    }


    /**
     * 接收消息 （接收来自移动端的消息）
     */
    //3.数据读取
    @Override
    public void channelRead0(ChannelHandlerContext ctx, Message message) throws Exception {
        //消息分类
        //1.mobile --> game 绑定娃娃机 game(client) --> control(server)
        //2.mobile --> game 控制娃娃机 上/下/左/右 game(client) --> control(server)
        //3.
        //logger.debug("host:{} 开始接收消息...", ((InetSocketAddress) ctx.channel().remoteAddress()).getHostName());
        //logger.debug("消息内容：" + new String(message.getData()));
        handlerMessage(new SessionImpl(ctx.channel()), message);
    }

    //4.数据读取完毕
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        super.channelReadComplete(ctx);
        //logger.debug("host:{} 消息接收完毕...", ((InetSocketAddress) ctx.channel().remoteAddress()).getHostName());
    }


    /**
     * 断线移除会话
     */
    //5.连接无效
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Session session = new SessionImpl(ctx.channel());
        Object object = session.getAttachment();
        if (object instanceof Player) {
            Player player = (Player) object;
            //long coinTime = player.getCoinTime();
            //int time = new Long((System.currentTimeMillis() - coinTime)/1000).intValue();
            SessionManager.removeSession(SessionManager.PREFIX_SESSION_KEY_USER + player.getId());
            logger.debug("Player「id={}, nickname={}」 断开远程连接.", player.getId(), player.getNickname(), ((InetSocketAddress) ctx.channel().remoteAddress()).getHostName());

        }

    }

    //6.连接注销
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        super.channelUnregistered(ctx);
        logger.debug("host:{} 远程连接已注销...", ((InetSocketAddress) ctx.channel().remoteAddress()).getHostName());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
        Channel channel = ctx.channel();
        if (!channel.isActive()) {
            logger.info("#######################--客户端-{}断开了长连接！", channel.remoteAddress());
            ctx.close();
        } else {
            ctx.fireExceptionCaught(cause);
            logger.error("#######################", cause);
        }
    }

    /**
     * 消息处理
     *
     * @param session
     * @param message
     */
    private void handlerMessage(Session session, Message message) {

        String sessionKey = null;
        Player player = null;
        switch (message.getType()) {
            //移动端用户连接
            case MsgConstants.USER_CONNECT:
                logger.debug("GET Message「Player」:" + new String(message.getData()));
                UserConnectMsg connectMsg = new Gson().fromJson(new String(message.getData()), UserConnectMsg.class);
                sessionKey = SessionManager.PREFIX_SESSION_KEY_USER + connectMsg.getUserId();
                if (connectMsg.getUserId() == null || "".equals(connectMsg.getUserId().trim())) {
                    UserConnectMsgAck connectMsgAck = new UserConnectMsgAck(connectMsg, "N");
                    SessionManager.sendMessage(sessionKey, connectMsgAck);
                    return;
                }
                player = playerService.findById(Integer.parseInt(connectMsg.getUserId()));
                if (null == player) {
                    UserConnectMsgAck connectMsgAck = new UserConnectMsgAck(connectMsg, "N");
                    SessionManager.sendMessage(sessionKey, connectMsgAck);
                    return;
                }

                //Token验证
                try {
                    if (null == connectMsg.getKey() || "".equals(connectMsg.getKey())) {
                        UserConnectMsgAck connectMsgAck = new UserConnectMsgAck(connectMsg, "N");
                        SessionManager.sendMessage(sessionKey, connectMsgAck);
                        return;
                    }
                    PlayRecord record = playRecordService.findByToken(connectMsg.getKey());
                    if (null == record) {
                        UserConnectMsgAck connectMsgAck = new UserConnectMsgAck(connectMsg, "N");
                        SessionManager.sendMessage(sessionKey, connectMsgAck);
                        return;
                    }
                    if (record.getTokenStatus().equals(PlayRecord.TOKEN_STATUS_USED)) {
                        UserConnectMsgAck connectMsgAck = new UserConnectMsgAck(connectMsg, "N");
                        SessionManager.sendMessage(sessionKey, connectMsgAck);
                        return;
                    }
                } catch (Exception e) {
                    logger.error("查询PlayRecord:", e);
                    UserConnectMsgAck connectMsgAck = new UserConnectMsgAck(connectMsg, "N");
                    SessionManager.sendMessage(sessionKey, connectMsgAck);
                    return;
                }
                //Token验证 end

                if (SessionManager.isOnline(sessionKey)) {
                    SessionManager.removeSession(sessionKey);
                }
                if (SessionManager.putSession(sessionKey, session)) {
                    session.setAttachment(player);

                    UserConnectMsgAck connectMsgAck = new UserConnectMsgAck(connectMsg, "Y");
                    SessionManager.sendMessage(sessionKey, connectMsgAck);
                    logger.debug("用户「user_id={},nickname={}」建立连接，token={}, deviceId={}, sessionKey={}", player.getId(), player.getNickname(), connectMsg.getKey(), connectMsg.getDeviceId(), sessionKey);
                }
                break;
            //移动端用户进行控制（投币/上/下/左/右）
            case MsgConstants.USER_CMD:
                logger.debug("GET Message「Player」:" + new String(message.getData()));
                UserCmdMsg ctrlMsg = new Gson().fromJson(new String(message.getData()), UserCmdMsg.class);
                sessionKey = SessionManager.PREFIX_SESSION_KEY_USER + ctrlMsg.getUserId();
                session = SessionManager.getSession(sessionKey);
                if (SessionManager.isOnline(sessionKey)) {
                    if (ctrlMsg.getCmd().equals(CmdConstants.DEVICE_CMD_COIN)) {
                        try {
                            //验证Token
                            if (null == ctrlMsg.getKey() || "".equals(ctrlMsg.getKey())) {
                                UserCmdMsgAck userCmdMsgAck = new UserCmdMsgAck(ctrlMsg, "N");
                                SessionManager.sendMessage(sessionKey, userCmdMsgAck);
                                return;
                            }
                            PlayRecord record = playRecordService.findByToken(ctrlMsg.getKey());
                            if (null == record) {
                                UserCmdMsgAck userCmdMsgAck = new UserCmdMsgAck(ctrlMsg, "N");
                                SessionManager.sendMessage(sessionKey, userCmdMsgAck);
                                return;
                            }
                            if (record.getTokenStatus().equals(PlayRecord.TOKEN_STATUS_USED)) {
                                UserCmdMsgAck userCmdMsgAck = new UserCmdMsgAck(ctrlMsg, "N");
                                SessionManager.sendMessage(sessionKey, userCmdMsgAck);
                                return;
                            }
                            //消费token
                            record.setTokenStatus(PlayRecord.TOKEN_STATUS_USED);
                            playRecordService.updatePlayRecord(record);
                            //playRecordService.updatePlayRecordForStartGame(record, ctrlMsg.getDeviceId());

                            if (null != session) {
                                Object object = session.getAttachment();
                                if (object instanceof Player) {
                                    Player p = (Player) object;
                                    if (p.getCoinTimeOutThread() != null) {
                                        logger.debug("存在超时线程，关闭超时线程！");
                                        p.getCoinTimeOutThread().interrupt();
                                        p.setCoinTimeOutThread(null);
                                    } else {
                                        logger.debug("未发现超时线程！");
                                    }
                                    p.setCoinTime(System.currentTimeMillis());
                                }
                            }
                        } catch (Exception e) {
                            logger.error("查询/修改PlayRecord:", e);
                            UserCmdMsgAck userCmdMsgAck = new UserCmdMsgAck(ctrlMsg, "N");
                            SessionManager.sendMessage(sessionKey, userCmdMsgAck);
                            return;
                        }
                    }
                    //发消息至control服
                    Main.client.sendMessage(new GameServerCmdMsg(ctrlMsg));
                    logger.debug("用户 {} 操作，命令号：{}", ctrlMsg.getUserId(), ctrlMsg.getCmd());
                } else {
                    UserCmdMsgAck ctrlMsgAck = new UserCmdMsgAck(ctrlMsg, "N");
                    SessionManager.sendMessage(sessionKey, ctrlMsgAck);
                }
                break;
            case MsgConstants.USER_DISCONNECT:
                logger.debug("GET Message「Player」:" + new String(message.getData()));
                UserDisconnectMsg userDisconnectMsg = new Gson().fromJson(new String(message.getData()), UserDisconnectMsg.class);
                sessionKey = SessionManager.PREFIX_SESSION_KEY_USER + userDisconnectMsg.getUserId();
                session = SessionManager.getSession(sessionKey);
                if (null != session) {
                    player = (Player) session.getAttachment();
                    //终止超时线程
                    if (null != player && null != player.getCoinTimeOutThread()) {
                        player.getCoinTimeOutThread().interrupt();
                    }
                } else {
                    player = playerService.findById(Integer.parseInt(userDisconnectMsg.getUserId()));
                }
                //修改娃娃机状态
                try {
                    WaWaMachine machine = waWaMachineService.findByDeviceId(userDisconnectMsg.getDeviceId());
                    if (null != machine && machine.getStatus() == WaWaMachine.STATUS_PROGRESS
                            && null != machine.getCurrentKey() && machine.getCurrentKey().equals(userDisconnectMsg.getKey())) {
                        machine.setStatus(WaWaMachine.STATUS_IDLE);
                        machine.setCurrentPlayerId(null);
                        machine.setCurrentKey(null);
                        waWaMachineService.updateMachine(machine);
                        pushGameOverToNotify(player, userDisconnectMsg.getDeviceId());
                    } else {
                        if (null == machine) {
                            logger.error("娃娃机「device_id={}」不存在", userDisconnectMsg.getDeviceId());
                        } else {
                            logger.error("娃娃机当前状态:「{}」", machine.getStatus());
                            logger.error("current_key:{}, disconnect_key:{}", machine.getCurrentKey(), userDisconnectMsg.getKey());
                        }

                    }
                } catch (Exception e) {
                    logger.error("用户主动断开连接，修改机器占用状态", e);
                }
                SessionManager.sendMessage(sessionKey, new UserDisconnectMsgAck(userDisconnectMsg, "Y"));
                SessionManager.removeSession(sessionKey);
                if (null != session)
                    session.close();
                break;

            //移动端断线重连
            case MsgConstants.USER_RECONNECT:
                logger.debug("GET Message「Player」:" + new String(message.getData()));
                UserReconnectMsg userReconnectMsg = new Gson().fromJson(new String(message.getData()), UserReconnectMsg.class);
                sessionKey = SessionManager.PREFIX_SESSION_KEY_USER + userReconnectMsg.getUserId();
                Session oldSession = SessionManager.getSession(sessionKey);
                if (null == oldSession) {
                    SessionManager.sendMessage(session, new UserReconnectMsgAck(userReconnectMsg, "N"));
                    return;
                } else {
                    player = (Player) oldSession.getAttachment();
                    //更新session
                    //session.setAttachment(player);
                    SessionManager.replaceSession(sessionKey, session);

                    long coinTime = player.getCoinTime();
                    Integer time = 30 - new Long((System.currentTimeMillis() - coinTime) / 1000).intValue();
                    SessionManager.sendMessage(sessionKey, new UserReconnectMsgAck(userReconnectMsg, time.toString(), "Y"));
                    logger.debug("PUT Message「Player」:" + new Gson().toJson(new UserReconnectMsgAck(userReconnectMsg, time.toString(), "Y")));
                }
                break;
            default:
                break;
        }
    }

    private void pushGameOverToNotify(Player player, String deviceId) {
        Jedis jedis = jedisPool.getResource();
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("device_id", deviceId);
        jsonObj.put("user_id", player.getId());
        jsonObj.put("nickname", player.getNickname());
        jsonObj.put("header_icon", player.getHeaderIcon());
        jedis.lpush("leave-queue", jsonObj.toString());
        jedis.close();
        logger.debug("推送消息至redis队列「key=leave-queue」:{}", jsonObj.toString());
    }

}